﻿using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

namespace RabbitMQClient
{
    public static class RpcServer
    {
        public static void RegisterRpcHandle<T, TResult>(this IRabbitMQClient client, string rpc_queue, Func<T, TResult> func)
            where T : class, new()
            where TResult : class, new()
        {
            var channel = client.GetConnection().CreateModel();
            channel.QueueDeclare(queue: rpc_queue, durable: false, exclusive: false, autoDelete: false, arguments: null);
            channel.BasicQos(0, 1, false);
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: rpc_queue, autoAck: false, consumer: consumer);

            consumer.Received += (model, ea) =>
            {
                TResult response = null;

                var body = ea.Body;
                var props = ea.BasicProperties;
                var replyProps = channel.CreateBasicProperties();
                replyProps.CorrelationId = props.CorrelationId;

                try
                {
                    var message = Encoding.UTF8.GetString(body);
                    T request = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(message);
                    response = func(request);
                }
                catch (Exception e)
                {
                    response = default(TResult);
                }
                finally
                {
                    string responseStr = Newtonsoft.Json.JsonConvert.SerializeObject(response);
                    var responseBytes = Encoding.UTF8.GetBytes(responseStr);
                    channel.BasicPublish(exchange: "", routingKey: props.ReplyTo, basicProperties: replyProps, body: responseBytes);
                    channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
                }
            };
        }
    }
}
